#
# This file is a part of the open-eBackup project.
# This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
# If a copy of the MPL was not distributed with this file, You can obtain one at
# http://mozilla.org/MPL/2.0/.
#
# Copyright (c) [2024] Huawei Technologies Co.,Ltd.
#
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
#

import sys
import os

from common.cleaner import clear
from common.const import ExecuteResultEnum, SysData
from sqlserver import log
from sqlserver.commons.common import output_action_result, check_del_dir_or_file
from sqlserver.commons.const import SQLServerType
from sqlserver.commons.const import BodyErr, SQLServerCode, ParamConstant
from sqlserver.sqlserver_restore_always_on import SQLServerRestoreAlwaysOn
from sqlserver.sqlserver_restore_cluster_instance import SQLServerRestoreClusterInstance
from sqlserver.sqlserver_restore_database import SQLServerRestoreDatabase
from sqlserver.sqlserver_restore_instance import SQLServerRestoreInstance
from sqlserver.sqlserver_base import SQLServerBase


class SQLServerRestoreComm:
    def __init__(self, pid):
        self.pid = pid
        self._sqlserver_object = None
        self._restore_type = ""

    @staticmethod
    def get_code(input_ret):
        if input_ret:
            return SQLServerCode.SUCCESS.value
        else:
            return SQLServerCode.FAILED.value

    @staticmethod
    def format_body_err(input_ret):
        tmp_body_err = 0
        if not input_ret:
            tmp_body_err = BodyErr.ERR_PLUGIN_CANNOT_BACKUP.value
        return tmp_body_err

    def create_restore_by_type(self, tmp_job_id, tmp_sub_job_id):
        file_path = os.path.join(ParamConstant.PARAM_FILE_PATH, f"param{self.pid}")
        try:
            json_param = SQLServerBase.read_temp_file(file_path)
        except Exception as e:
            log.error(f"Read param file failed, err mgs is:{str(e)}")
            return False
        finally:
            check_del_dir_or_file(file_path)
        self._restore_type = json_param.get("job", {}).get("targetObject", {}).get("subType")
        if self._restore_type == SQLServerType.SQL_SERVER_DATABASE:
            self._sqlserver_object = SQLServerRestoreDatabase(self.pid, tmp_job_id, tmp_sub_job_id, json_param)
        elif self._restore_type == SQLServerType.SQL_SERVER_INSTANCE:
            self._sqlserver_object = SQLServerRestoreInstance(self.pid, tmp_job_id, tmp_sub_job_id, json_param)
        elif self._restore_type == SQLServerType.SQL_SERVER_ALWAYS_ON:
            self._sqlserver_object = SQLServerRestoreAlwaysOn(self.pid, tmp_job_id, tmp_sub_job_id, json_param)
        elif self._restore_type == SQLServerType.SQL_SERVER_CLUSTER_INSTANCE:
            self._sqlserver_object = SQLServerRestoreClusterInstance(self.pid, tmp_job_id, tmp_sub_job_id, json_param)
        else:
            log.error(f"Restore type is wrong: {self._restore_type}")
            return False
        return True

    def allow_restore_in_local_node(self):
        try:
            tmp_ret = self._sqlserver_object.allow_restore_in_local_node()
            if tmp_ret:
                output_action_result(self.pid, self.get_code(tmp_ret), self.format_body_err(tmp_ret), "")
            return tmp_ret
        except Exception as exception:
            output_action_result(self.pid, SQLServerCode.FAILED.value, BodyErr.ERROR_INTERNAL.value, "")
            log.error(f"Exception when exec allow in local as exception: {exception}.")
        return False

    def restore_prerequisite(self):
        tmp_ret = self._sqlserver_object.exec_restore_pre_job()
        output_action_result(self.pid, self.get_code(tmp_ret), self.format_body_err(tmp_ret), "")
        return tmp_ret

    def gen_sub_job(self):
        tmp_ret = self._sqlserver_object.exec_gen_sub_job()
        if not tmp_ret:
            output_action_result(self.pid, self.get_code(tmp_ret), self.format_body_err(tmp_ret), "")
            return False
        return True

    def restore(self):
        tmp_ret = self._sqlserver_object.exec_sub_job()
        output_action_result(self.pid, self.get_code(tmp_ret), self.format_body_err(tmp_ret), "")
        return True

    def restore_post(self):
        tmp_ret = self._sqlserver_object.exec_restore_post()
        output_action_result(self.pid, self.get_code(tmp_ret), self.format_body_err(tmp_ret), "")
        return tmp_ret

    def report_progress_comm(self):
        return self._sqlserver_object.report_progress_comm()

    def report_restore_progress(self):
        return self._sqlserver_object.report_restore_progress()

    def query_job_permission(self):
        output_action_result(self.pid, ExecuteResultEnum.SUCCESS, ExecuteResultEnum.SUCCESS, "")
        return True


def func_exec(func_array, pid, job_id):
    try:
        ret = func_array[0]()
        return ret
    except Exception as exception:
        log.error(f"Exec cmd failed as exception: {str(exception)} pid: {pid} jobId: {job_id}.")
    finally:
        clear(SysData.SYS_STDIN)
    return False


def do_work(args_list):
    cmd = args_list[0]
    pid = args_list[1]
    job_id = ""
    sub_job_id = ""
    if len(args_list) >= 3:
        job_id = args_list[2]
    if len(args_list) == 4:
        sub_job_id = args_list[3]
    restore_comm_inst = SQLServerRestoreComm(pid)
    if not restore_comm_inst.create_restore_by_type(job_id, sub_job_id):
        output_action_result(pid, SQLServerCode.FAILED.value, BodyErr.ERROR_COMMON_INVALID_PARAMETER.value,
                             f"Cmd Param error.")
        return False
    cmd_dict = {
        "AllowRestoreInLocalNode": [restore_comm_inst.allow_restore_in_local_node],
        "RestorePrerequisite": [restore_comm_inst.restore_prerequisite],
        "RestoreGenSubJob": [restore_comm_inst.gen_sub_job],
        "QueryJobPermission": [restore_comm_inst.query_job_permission],
        "Restore": [restore_comm_inst.restore],
        "RestorePost": [restore_comm_inst.restore_post],
        "progress_comm": [restore_comm_inst.report_progress_comm],
        "progress_restore": [restore_comm_inst.report_restore_progress]
    }
    func_array = cmd_dict.get(cmd)
    if not func_array:
        output_action_result(pid, SQLServerCode.FAILED.value, BodyErr.ERROR_COMMON_INVALID_PARAMETER.value,
                             f"Cmd Param error.")
        return False
    if cmd in ["progress_comm", "progress_restore"]:
        try:
            ret = func_array[0]()
            if not ret:
                raise Exception("Exec restore progress failed, ret is false.")
            return ret
        finally:
            clear(SysData.SYS_STDIN)
    else:
        return func_exec(func_array, pid, job_id)


if __name__ == '__main__':
    for line in sys.stdin:
        SysData.SYS_STDIN = line
        break
    args = sys.argv[1:]
    log.info(f"Call restore script paramCnt:{len(args)} args:{args}")
    if len(args) < 2:
        log.error(f"No enough parameters, param cnt:{len(args)}")
        sys.exit(1)
    try:
        result = do_work(args)
        log.debug(f"Exec do restore work finished, result: {result}, args:{args}.")
        if not result:
            sys.exit(0)
    except Exception as err:
        log.error(f"Exec do restore work failed as err: {str(err)}, args:{args}.")
        sys.exit(1)

